"use strict";
/**
 * 描述一个二维的空间直线
 * 直线方程为 y=a+bx
 * 在本模型中,y为因变量,x为自变量
 */
class Line2d {
    constructor(a, b) {
        this.a = null;
        this.b = null;
        this.a = a;
        this.b = b;
    }
    /** po
     * 获取自变量
     */
    getIndependentVar(y) {
        return (y - this.a) / this.b;
    }
    /** 获取因变量 */
    getDependentVar(x) {
        return this.a + this.b * x;
    }
}
/**
 * 2d直线的线性回归函数
 * @param {Array} xArray 先导入的为自变量
 * @param {Array} yArray 后导入的为因变量
 */
function line2dFit(xArray, yArray) {
    var xSum = 0, ySum = 0, xySum = 0, xSquareSum = 0;
    var n = xArray.length; //点的长度
    for (var i = 0; i < n; i++) {
        xSum += xArray[i];
        ySum += yArray[i];
        xySum += xArray[i] * yArray[i];
        xSquareSum += xArray[i] * xArray[i];
    }
    // e = y-a-bx
    // ∑e^2 = ∑(y-a-bx)^2 , 回归模型为 min(∑e^2),即e的平方和的最小值
    // 上式分别对a,b求偏导,使之各等于0,再解二元一次方程:
    // 不服不辩,参考网址 http://blog.csdn.net/qll125596718/article/details/8248249    
    var b = (n * xySum - xSum * ySum) / (n * xSquareSum - xSum * xSum);
    var a = (xSquareSum * ySum - xSum * xySum) / (n * xSquareSum - xSum * xSum);
    return new Line2d(a, b);
}
/**
 * 3d直线求线性回归
 * @param {Array} xyzArray 包含xyz数组的数组,如[[2,3,5],[3,6,7]]
 */
module.exports = function line3dFit(xyzArray) {
    var xArray = xyzArray.map(item => item[0]);
    var yArray = xyzArray.map(item => item[1]);
    var zArray = xyzArray.map(item => item[2]);
    //3d直线拟合的思路如下:
    //先在XOY平面上,拟合直线,该直线 y = a + bx 在三维坐标系下是一个垂直于XOY平面的面
    //再到YOZ平面上,拟合直线,该直线 z = c + dy 在三维坐标系下是一个垂直于YOZ平面的面
    //这两个面在空间上相交的直线就为该3d直线
    var plateXY = line2dFit(xArray, yArray);
    var plateYZ = line2dFit(yArray, zArray);
    var line3d = new Line3d(plateXY, plateYZ, xArray);
    return line3d;
};
class Line3d {
    /**
     * xArray应该是沿里程方向上的坐标数组
     */
    constructor(plate1, plate2, xArray) {
        /** 该段直线的里程长度 */
        this.mileageLength = 0;
        this.plateXY = plate1;
        this.plateYZ = plate2;
        //以xArray两端的数据,在直线上寻找对应的2点
        var x0 = xArray[0];
        var y0 = this.plateXY.getDependentVar(x0);
        var z0 = this.plateYZ.getDependentVar(y0);
        var x1 = xArray[xArray.length - 1];
        var y1 = this.plateXY.getDependentVar(x1);
        var z1 = this.plateYZ.getDependentVar(y1);
        this.startXYZ = [x0, y0, z0];
        this.endXYZ = [x1, y1, z1];
        // 则这条直线可以描述为经过[x0,y0,z0],[x1,y1,z1]的直线,2点式方程可以写为:
        // x - x0 / x1 - x0= y - y0 / y1 - y0 =z - z0 / z1 - z0,计算后可写为参数式方程
        // (x - x0) / m = (y - y0) / n =(z - z0) / p
        this.m = x1 - x0;
        this.n = y1 - y0;
        this.p = z1 - z0;
        this._normalizeMNP(); //归一化mnp
    }
    /**
     * 向量归一化,应使 m * m + n * n + p * p=1
     */
    _normalizeMNP() {
        var squareSum = this.m * this.m + this.n * this.n + this.p * this.p;
        var root = Math.sqrt(squareSum);
        //本段直线的里程长度
        this.mileageLength = root;
        this.m = this.m / root;
        this.n = this.n / root;
        this.p = this.p / root;
    }
    /**
     * 通过里程上的长度,求该点的xyz
     * @return 返回三维坐标数组 [x,y,z]
     */
    getXYZ(mileageDiff) {
        var x = this.startXYZ[0] + mileageDiff * this.m;
        var y = this.startXYZ[1] + mileageDiff * this.n;
        var z = this.startXYZ[2] + mileageDiff * this.p;
        return [x, y, z];
    }
}
